<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha256-AbA177XfpSnFEvgpYu1jMygiLabzPCJCRIBtR5jGc0k=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"fengxun2017.github.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.13.2","exturl":false,"sidebar":{"position":"right","display":"hide","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="基于cortex-m3&#x2F;m4内核，介绍FreeRTOS自己抽象出来的任务优先级概念，以及FreeRTOS任务调度涉及的硬件中断优先级。">
<meta property="og:type" content="article">
<meta property="og:title" content="FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV）">
<meta property="og:url" content="http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/index.html">
<meta property="og:site_name" content="fx">
<meta property="og:description" content="基于cortex-m3&#x2F;m4内核，介绍FreeRTOS自己抽象出来的任务优先级概念，以及FreeRTOS任务调度涉及的硬件中断优先级。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/ready-queue.jpg">
<meta property="og:image" content="http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/high_pendsv_error.png">
<meta property="article:published_time" content="2022-12-21T16:00:00.000Z">
<meta property="article:modified_time" content="2022-12-25T10:18:56.964Z">
<meta property="article:author" content="feng xun">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/ready-queue.jpg">


<link rel="canonical" href="http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/","path":"2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/","title":"FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV）"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV） | fx</title>
  






  <script async defer data-website-id="" src=""></script>

  <script defer data-domain="" src=""></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
  <a target="_blank" rel="noopener" href="https://github.com/fengxun2017" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#70B7FD; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">fx</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li>
  </ul>
</nav>




</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-%E4%BB%BB%E5%8A%A1%E4%BC%98%E5%85%88%E7%BA%A7"><span class="nav-number">1.</span> <span class="nav-text">1 任务优先级</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-%E5%86%85%E6%A0%B8%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6%E7%A1%AC%E4%BB%B6%E4%B8%AD%E6%96%AD%E4%BC%98%E5%85%88%E7%BA%A7"><span class="nav-number">2.</span> <span class="nav-text">2 内核任务调度硬件中断优先级</span></a><ol class="nav-child"><li class="nav-item nav-level-5"><a class="nav-link" href="#2-1-PendSV%E4%BC%98%E5%85%88%E7%BA%A7%E8%AE%BE%E7%BD%AE%E6%88%90%E6%9C%80%E4%BD%8E%E7%9A%84%E4%B8%80%E4%BA%9B%E5%8E%9F%E5%9B%A0%EF%BC%9A"><span class="nav-number">2.1.</span> <span class="nav-text">2.1 PendSV优先级设置成最低的一些原因：</span></a></li><li class="nav-item nav-level-5"><a class="nav-link" href="#2-2-SyStick%E4%BC%98%E5%85%88%E7%BA%A7%E8%AE%BE%E7%BD%AE%E6%88%90%E6%9C%80%E4%BD%8E%E7%9A%84%E4%B8%80%E4%BA%9B%E5%8E%9F%E5%9B%A0%EF%BC%9A"><span class="nav-number">2.2.</span> <span class="nav-text">2.2 SyStick优先级设置成最低的一些原因：</span></a></li></ol></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E6%80%BB%E7%BB%93"><span class="nav-number">3.</span> <span class="nav-text">总结</span></a></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">feng xun</p>
  <div class="site-description" itemprop="description">FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838</div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">96</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">16</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
  </nav>
</div>
  <div class="cc-license site-overview-item animated" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" class="cc-opacity" rel="noopener" target="_blank"><img src="https://cdnjs.cloudflare.com/ajax/libs/creativecommons-vocabulary/2020.11.3/assets/license_badges/small/by_nc_sa.svg" alt="Creative Commons"></a>
  </div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="feng xun">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="fx">
      <meta itemprop="description" content="FreeRTOS交流QQ群-663806972 BLE学习交流群-498676838">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV） | fx">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV）
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-12-22 00:00:00" itemprop="dateCreated datePublished" datetime="2022-12-22T00:00:00+08:00">2022-12-22</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/FreeRTOS/" itemprop="url" rel="index"><span itemprop="name">FreeRTOS</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>7.9k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>10 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
        <center>
基于cortex-m3/m4内核，介绍FreeRTOS自己抽象出来的任务优先级概念，以及FreeRTOS任务调度涉及的硬件中断优先级。

</center>

<span id="more"></span>

<hr>
<h4 id="1-任务优先级"><a href="#1-任务优先级" class="headerlink" title="1 任务优先级"></a>1 任务优先级</h4><p>FreeRTOS中的任务优先级，是FreeRTOS自己抽象出来的概念。只作用于FreeRTOS创建出来的任务。FreeRTOS内核每次进行任务调度时，总是选择优先级最高的就绪任务来运行。</p>
<p>在工程配置文件<code>FreeRTOSConfig.h</code>中定义了当前系统中，允许使用的任务优先级数量：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> configMAX_PRIORITIES		( 3 )</span></span><br></pre></td></tr></table></figure>
<p>即，使用<code>xTaskCreate</code>时传递的优先级参数<code>uxPriority</code>的值应该在<code>[0,3)</code>中，值越大表示任务的优先级越大。</p>
<p>任务优先级的唯一作，就是内核在每次调度任务时会选择优先级最高的就绪任务来运行（如果有多个任务的优先级相同，且都是当前最大的，默认是轮流调度）。</p>
<p>在内核的实现中，会根据宏定义<code>configMAX_PRIORITIES</code>的值，创建对应数量的<code>就绪任务队列</code>，例如<code>configMAX_PRIORITIES = 3</code>时，会创建3个就绪任务队列。例如，当我们创建一个优先级为 2 的任务 A。两个优先级为 1 的任务 B 和 C，以及一个优先级为 0 的任务 D ，则<code>就绪任务队列</code>的状态如下：<br><img src="/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/ready-queue.jpg"><br>这样做的好处时，每个任务在创建时，直接挂到对应优先级任务队列上即可。而内核在选择任务调度时，直接从有任务的最高优先级任务队列上选择即可（会选择这个任务队列中最近一次所运行任务的下一个任务，内核的任务链表结构中，有一个成员指向本任务队列（链表结构）中最近运行的那个任务，如此即可实现同一优先级的任务间轮流调度）。</p>
<p>更详细的任务调度和管理信息，可以参考文章：<a href="https://fengxun2017.github.io/2022/11/01/FreeRTOS-task-status-management/#%E5%B0%B1%E7%BB%AA%E4%BB%BB%E5%8A%A1%E9%98%9F%E5%88%97%EF%BC%9A">任务调度和管理-就绪任务队列</a><br>而向任务队列中插入新任务，从任务队列中获取下一个要运行的任务，可以参考文章<a href="https://fengxun2017.github.io/2022/11/04/FreeRTOS-kernel-list/#%E6%80%BB%E7%BB%93">FreeRTOS-内核链表数据结构</a> 最后的总结部分。</p>
<h4 id="2-内核任务调度硬件中断优先级"><a href="#2-内核任务调度硬件中断优先级" class="headerlink" title="2 内核任务调度硬件中断优先级"></a>2 内核任务调度硬件中断优先级</h4><p>FreeRTOS任务切换使用的硬件中断优先级，是工程配置文件<code>FreeRTOSConfig.h</code>文件中设置的宏<code>configKERNEL_INTERRUPT_PRIORITY</code> 。</p>
<p>该值定义了FreeRTOS的 <code>tick</code>中断的优先级。 在cortex-m3&#x2F;m4 平台上，FreeRTOS默认使用<code>SysTick</code>定时器（所有基于cortex-m3&#x2F;m4 的MCU中都会有这个定时器，所以很方便移植）来产生<code>tick</code>中断（内核运作的<code>心跳</code>，每次tick中断发生时，会在中断处理函数中执行任务调度相关工作）。所以，在 cortex-m3&#x2F;m4 平台上，<strong>宏<code>configKERNEL_INTERRUPT_PRIORITY</code>的值就是<code>SysTick</code>定时器的中断优先级</strong>。</p>
<p>每当<code>tick</code>中断周期到达，内核需要做的事情有：</p>
<ul>
<li>更新内部“时间计数tick值”</li>
<li>遍历delay任务队列将等待时间超时的任务恢复就绪状态（例如，任务调用<code>vTaskDelay</code>延迟的时间到达了，或任务调用<code>xQueueReceive</code>等待消息队列的超时时间到达了）。</li>
<li>检查是否需要进行任务切换，执行任务切换等等工作。</li>
</ul>
<p>但上述全部工作实际是由<code>SysTick</code>和<code>PendSV</code>两个中断共同完成的。<br><code>SysTick</code>中断中只完成了部分工作，将内核用来记录时间的变量<code>xTickCount</code>加1；将延迟队列<code>pxDelayedTaskList</code>中超时的任务挂回就绪队列中；根据内部状态和当前系统配置（如是否抢占调度，同优先级是否时间片调度（轮流调度））决定是否执行任务切换。如果检查确认了本次需要执行任务切换，内核就会设置”挂起”<code>PendSV</code>中断，并退出<code>systick</code>中断处理函数。</p>
<p>当退出的<code>systick</code>中断处理函数后，就会进入<code>PendSV</code>中断（因为有中断“挂起”标记在）处理函数中，并执行实际的任务切换工作（选择当前最高优先级的就绪任务，并运行它）。<br><strong>这里的<code>PendSV</code>中断，其中断优先级也是 宏<code>configKERNEL_INTERRUPT_PRIORITY</code>定义的值</strong>。</p>
<p><strong>即，<code>SysTick</code>和<code>PendSV</code>两个与内核任务调度相关的中断，它们的的中断优先级都是宏<code>configKERNEL_INTERRUPT_PRIORITY</code>定义的值。FreeRTOS默认都是将它们设置成最低中断优先级</strong>。</p>
<p>在《ARM Cortex-m3&#x2F;m4权威指南》一书中，介绍的<code>SysTick</code>和<code>PendSV</code>配合实现任务切换，是将<code>SysTick</code>中断优先级设置成高优先级，<code>PendSV</code>设置成最低优先级。这里与FreeRTOS中实际使用设置的不同，往往给初学者造成困惑。</p>
<p>首先结论是： <strong>书中的方案和FreeRTOS的方案，都是正确的，甚至即使是将PendSV的优先级设置为高优先级都是可以的，但是PendSV设置成高优先级虽然可以，但并不合理</strong>。所以，实际使用中都是将<code>PendSV</code>的中断优先级设置为最低。至于<code>SysTick</code>，可以设置为高优先级（官网内核的<code>SysTick</code>中断处理函数，默认是基于最低优先级实现的，调高则需要一些改动，见后文描述），也可以设置为最低优先级，具体采用什么方案，开发者可以根据自己的实际应用需求而设定（例如，你没有额外的硬件定时器资源了，所以你复用了FreeRTOS内核使用的<code>SysTick</code>定时器，并在<code>SysTick</code>中断处理函数中添加一些自己的代码逻辑，而这些代码逻辑依赖精确的时间，所以需要将<code>SysTick</code>中断优先级调高，避免被其它中断抢占，导致响应延迟）。</p>
<p><strong>那么，为什么FreeRTOS在默认都是将<code>SysTick</code>和<code>PendSV</code>中断优先级设置成最低呢</strong>？个人整理了以下一些原因。</p>
<h5 id="2-1-PendSV优先级设置成最低的一些原因："><a href="#2-1-PendSV优先级设置成最低的一些原因：" class="headerlink" title="2.1 PendSV优先级设置成最低的一些原因："></a>2.1 <code>PendSV</code>优先级设置成最低的一些原因：</h5><ul>
<li><p>首先，内核并不只是在每次<code>tick中断</code>发生时才执行任务切换。当我们使用抢占式调度时，新建一个高优先级的任务；调用<code>vTaskDelay</code>让任务延迟；等待某个资源（等待消息队列有消息，等待信号量可用等）；任务向消息队列中发送一个消息等等，很多操作都可能会触发任务切换工作，所以单独使用<code>PendSV</code>中断来做任务切换工作。这样在所有需要执行任务切换的地方，设置“挂起”<code>PendSV</code>中断，即可触发执行任务切换。</p>
</li>
<li><p><code>PendSV</code>中断处理函数中只做了任务切换工作（选择当前最高优先级的就绪任务，并运行它），并不涉及到内核中和“时间”相关处理的工作。因此它的中断优先级设置为最低没什么影响，因为即使<code>PendSV</code>中断被其他高优先级中断抢占，导致执行任务切换延迟了，又有什么关系呢？反正高优先级中断服务结束后，还是可以继续完成任务切换的（中断处理函数中只做最重要和必须的事情，并尽快退出中断处理函数，这是一个基本共识。所以即使有高优先级中断抢占，造成任务切换延迟，这个延迟也不高。总不能你的程序逻辑，依赖于在精确时间点执行任务切换吧？）。反之，将<code>PendSV</code>优先级设置成高优先级（需要额外的内核配置才行，下文有详细说明），那么<code>PendSV</code>就可能抢占同时发生的其他 IRQ，导致被抢占的 IRQ响应出现延迟。而我们使用其它 IRQ的目的，都是为了做一些需要即时处理的重要工作（如按键中断处理，数据接收中断处理等），相对“不重要”的任务切换可能会抢占了“更重要”的对外响应，这是不合理的。所以将<code>PendSV</code>优先级设置为最低，更合理。这样可以尽可能地减小任务切换中断对我们开发的其它功能的影响。</p>
</li>
<li><p>在Cortex-M3&#x2F;m4 内核的默认配置下，当除了目前正在处理的中断，还有其它中断处于活动状态时，（当前处理的中断结束时）只能从Handler模式（执行中断处理函数时所处的模式）转到Handler模式，不允许直接从Handler模式转到Thread模式（普通代码执行时的模式）。在ARM7架构(cortex-m3&#x2F;m4就是arm7架构)的参考手册《Armv7-M Architecture Reference Manual》的 B1.5.8 Exception return behavior 一节中的Integrity checks on exception return 部分有相关描述：<br>  <code>Normally, if at least one exception other than the returning exception is active, the return must be to Handler  mode. This checks for a mismatch of the number of exception returns. Software can use the  CCR.NONBASETHRDENA to disable this check, see Configuration and Control Register, CCR on  page B3-604.</code><br>  基于这个限制，如果我们将<code>PendSV</code>的优先级设置为比某个外部中断优先级高，当在运行任务<code>task_a</code>时，外部中断请求（IRQ）到来，进入对应IRQ中断处理函数，<strong>该中断处理函数还未执行完成时，又发生任务切换中断<code>PendSV</code><strong>。<code>PendSV</code>优先级更高，抢占当前这个IRQ，在PendSV中断处理函数中执行任务切换，并运行任务 task_b，这会使得处理器从Handler模式切换成Thread模式。</strong>但此时被抢占的那个IRQ都还未运行完成</strong>，这种情况下，是不允许直接从Handler模式切换成Thread模式的！<strong>否则，硬件会产生<code>UsageFault</code>异常，如果你没有使能这个异常中断，则会升级为<code>HardFault</code>异常</strong>，如下图所示：<br>  <img src="/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/high_pendsv_error.png"><br><strong>这也是默认配置下，<code>PendSV</code>必须是最低优先级的原因</strong>。这个异常也很容易验证，你可以随便创建几个任务，然后将<code>configKERNEL_INTERRUPT_PRIORITY</code>这个值设置成更高的中断优先级，然后配置一个较低优先级的按键中断。在按键中断处理程序中，你原地循环保持一段时间，这样当<code>Tick</code>周期到来时，会触发<code>PendSV</code>中断来执行切换任务，<code>PendSV</code>中断会抢占当前的按键中断，当<code>PendSV</code>中断处理函数执行完任务切换过程，并返回到被选中的任务时（从Handler模式返回Thread模式），就会触发上述的异常（因为，此时按键中断都没结束，不允许在有其它中断还处于活动状态时，从Handler模式切换到Thread模式）。</p>
<p>既然是默认配置，那么就是可以修改。即修改成允许在还有其它中断处于活动状态下，当前正在执行的中断服务结束时，直接从Handler模式转到Thread模式。《Armv7-M Architecture Reference Manual》中也提到了修改方式，将<code>CCR寄存器</code>的<code>NONBASETHRDENA</code>位设置为 1即可。如下所示，在main函数中可以这样配置下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> CCR_CTRL_REG     ( *( ( volatile uint32_t * ) 0xE000ED14 ) )</span></span><br><span class="line">CCR_CTRL_REG |= <span class="number">1</span>;</span><br></pre></td></tr></table></figure>
<p>此时，同样是上面提高<code>PendSV</code>中断优先级的例子，就不会在产生<code>HardFault</code>异常中断了。（如果你花点时间折腾折腾，你会发现，如果你创建三个优先级相同的任务，然后修改按键中断服务函数，让其在原地循环久一点，比如3秒钟。当你按键时，在按键中断服务程序中的这 3秒钟时间内，内核是在 三个任务中的 某两个任务和按键中断服务程序之间来回调度。因为，基于FreeRTOS的任务切换汇编实现，按键中断服务被<code>PendSV</code>打断时，按键中断服务的“上下文数据”实际被保存到<code>某个任务</code>的任务栈中去了，所以内核虽然在三个任务中来回调度，但实际上调度到<code>某个任务</code>时，实际运行的是还未完成的按键中断服务。所以造成了<code> 3秒钟时间内，内核是在 三个任务中的 某两个任务和按键中断服务程序之间来回调度</code>这个现象。）<br>所以，虽然通过配置，也能允许<code>PendSV</code>优先级调高，但是此时，一旦发生了<code>PendSV</code>抢占其它IRQ，由于IRQ的“中断现场”被保存到了某个任务中的，那么IRQ恢复运行，就需要内核调度到该任务，这造成了改IRQ的延迟是系统 <code>tick</code>周期（而<code>tick</code>周期一般可能设置的比较大，如10ms，甚至100ms）的倍数。 这种延迟很恐怖，会严重影响系统运行效率，甚至导致某个功能异常。例如刚好按键中断优先级设置的比<code>PendSV</code>低，这种延迟下，按键也没法判断了。</p>
</li>
<li><p>综上，无论由于处理器的默认配置，要求<code>PendSV</code>中断优先级必须最低。还是从系统运行效率和正确性上来说，<code>PendSV</code>中断优先级都应该设置为最低。毕竟，我们不能因为使用了RTOS（初衷是为了提高开发效率和代码维护性），反而导致产品的实际功能被RTOS干扰（<code>PendSV</code>中断优先级被配置成比一些功能使用的IRQ优先级更高时）。</p>
</li>
</ul>
<h5 id="2-2-SyStick优先级设置成最低的一些原因："><a href="#2-2-SyStick优先级设置成最低的一些原因：" class="headerlink" title="2.2 SyStick优先级设置成最低的一些原因："></a>2.2 <code>SyStick</code>优先级设置成最低的一些原因：</h5><p>相比<code>PendSV</code>中断来说，<code>SysTick</code>中断的优先级就灵活很多，设置成高优先级或优先级都是可以的。<code>SysTick</code>中断处理函数中会更新内部“时间计数tick值”，变更一些任务状态等，最后时检查是否需要进行任务切换，需要任务切换的话，设置<code>PendSV</code>中断“挂起”就行了。</p>
<p>但是，<code>SysTick</code>中断服务程序中做的这些工作，都是为FreeRTOS内核做的，并不是直接和我们要实现的功能关联。我们引入FreeRTOS只是为了提高开发效率和代码的可维护性，所以FreeRTOS是作为一个辅助工具（毕竟不用RTOS，直接裸机开发也是可以的）。 因此，如果我们将<code>SysTick</code>的中断优先级设置的比较高的话，由于<code>SysTick</code>周期触发，那么很可能会经常抢占我们自己使用的其它中断（我们自己使用的其它中断，肯定是为了处理一些需要即时响应的工作）。辅助工具的运作，可能会经常打断我们本身功能执行的“重要工作”，这是不合理的。因此，<strong>FreeRTOS 默认将<code>SysTick</code>中断设置为最低优先级，这样默认情况下FreeRTOS的运作对系统其它“重要工作”（我们自己使用的IRQ）的影响最小</strong>。</p>
<p><code>SysTick</code>最低优先级可能导致<code>SysTick</code>的中断响应出现延迟，但这对FreeRTOS的正常运作并没有什么关系。<strong>如果你的功能依赖高精度的时间，首先考虑的应该是使用额外的硬件定时器来实现（并将其中断设置成高优先级）</strong>，而不是依赖FreeRTOS提供的一些时间相关API。例如，FreeRTOS提供的<a href="https://fengxun2017.github.io/2022/11/16/FreeRTOS-use-timer/">软件定时器</a>，虽然它可以满足大部门的应用对定时器的需求，但对于一些需要严格依赖us级时间精度的功能，并不适用（甚至对于一些依赖 低ms级时间精度的功能也不适用）。毕竟软件定时器依赖内核的<code>Tick</code>周期，这就决定了软件定时器能识别的时间粒度。此外，软件定时器还依赖定时器服务任务（或称为daemon task）的优先级（优先级高，该任务才会优先调度，并执行设置的超时回调函数），涉及消息通信，甚至还会被其它软件定时器干扰（另一个超时时间更短的软件定时器超时触发后，在超时回调函数中调用了诸如<code>vTaskDelay</code>等会让当前任务进入阻塞态的API，那么软件定时器服务任务在阻塞期间都不会被内核调度了，这期间超时的定时器都无法即时执行超时回调函数，毕竟运行这些回调函数的定时器服务任务都处于阻塞状态中。<code>所以，不要在软件定时器的超时回调函数中调用会让任务阻塞的API</code>）。</p>
<p>另一种情况是，没有额外的硬件定时器可用了。你复用了FreeRTOS用来产生<code>Tick</code>中断的定时器，（默认）即<code>SysTick</code>定时器。你在<code>SysTick</code>中断服务程序中添加了一些自己的功能代码，并且该功能依赖高精度的时间。这种情况下，你可以根据需要调高<code>SysTick</code>的中断优先级。但是，<strong>FreeRTOS中有一个需要注意的点</strong>（我使用的内核是官网的V10.4.6，不清楚其它版本有没有考虑这个问题），<code>SysTick</code>中断处理函数中的实现其实并未考虑中断嵌套问题，即它没有考虑<code>SysTick</code>是高优先级，然后抢占低优先级中断的情况。这在Cortex-m3&#x2F;m4 的<code>SysTick</code>中断处理函数中也有注释：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">xPortSysTickHandler</span><span class="params">( <span class="type">void</span> )</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">/* The SysTick runs at the lowest interrupt priority, so when this interrupt</span></span><br><span class="line"><span class="comment">     * executes all interrupts must be unmasked.  There is therefore no need to</span></span><br><span class="line"><span class="comment">     * save and then restore the interrupt mask value as its value is already</span></span><br><span class="line"><span class="comment">     * known. */</span></span><br><span class="line">    portDISABLE_INTERRUPTS();</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">/* Increment the RTOS tick. */</span></span><br><span class="line">        <span class="keyword">if</span>( xTaskIncrementTick() != pdFALSE )</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">/* A context switch is required.  Context switching is performed in</span></span><br><span class="line"><span class="comment">             * the PendSV interrupt.  Pend the PendSV interrupt. */</span></span><br><span class="line">            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    portENABLE_INTERRUPTS();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p> <strong>函数最开始的注释，就说明了该中断处理函数是是基于<code>SysTick</code>中断优先级是最低的情况</strong>。 这是因为它的屏蔽&#x2F;打开中断没有考虑，如果<code>SysTick</code>是打断低优先级中断的情况，这种情况下因该使用<code>portSET_INTERRUPT_MASK_FROM_ISR</code>来屏蔽中断，并保存返回的值，恢复中断时应该使用<code>portCLEAR_INTERRUPT_MASK_FROM_ISR</code>，并使用前面保存的返回值。如此，当返回到被打断的低优先级中断服务程序中时，才能保持中断屏蔽状态和被打断前是一样的。（具体可以全局搜<code>portSET_INTERRUPT_MASK_FROM_ISR</code>，看内核时如何使用这个宏的就清楚了）</p>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>任务优先级与硬件没有任何关系，这是FreeRTOS自己抽象出来的概念。在FreeRTOS进行任务调度时，总是选择优先级最高的就绪任务来运行。</p>
<p>FreeRTOS的任务调度需要使用硬件中断，涉及<code>SysTIck</code>定时器中断和<code>PendSV</code>中断。<br>对于<code>PendSV</code>中断，在处理器默认配置下（除了当前正在处理的中断，还有其它中断处于活动状态下时，只能从Handler模式转到Handler模式，不允许直接从Handler模式转到Thread模式），<code>PendSV</code>中断优先级必须设置为最低，否则，<code>PendSV</code>中断就有可能打断其它中断的处理，并在该中断还保持活动的状态下，（<code>PendSV</code>中断处理结束时）直接从Handler模式切换到Thread模式，从而触发异常。<br>虽然特殊配置下（<code>CCR寄存器</code>的<code>NONBASETHRDENA</code>位设置为 1），<code>PendSV</code>可以在还有其它中断处于活动状态下，直接从Handler模式转到Thread模式，但可能会导致其它IRQ运行效率很低（当<code>PendSV</code>抢占它们时），甚至功能异常（延迟太高，造成功能异常，如按键中断）。所以<code>PendSV</code>优先级设置最低是最好的选择。</p>
<p>另一方面，FreeRTOS只是辅助开发的工具，为了让该工具的运行尽可能少的影响我们实际开发的功能，所以FreeRTOS默认将<code>SysTick</code>，设置为最低优先级，这样可以对系统的其它功能影响最少。当然，如果你完全理解了上文所述的关于中断优先级变高需要做的修改，和会造成的影响，仍旧选择将<code>SysTick</code>优先级变高，这也是可以的，根据你的实际需求来设置即可！</p>
<br>
FreeRTOS交流QQ群-663806972

<br>

    </div>

    
    
    

    <footer class="post-footer">
          

<div class="post-copyright">
<ul>
  <li class="post-copyright-author">
      <strong>本文作者： </strong>feng xun
  </li>
  <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/" title="FreeRTOS-任务优先级和内核任务调度中断优先级（SysTick，PendSV）">http://fengxun2017.github.io/2022/12/22/FreeRTOS-task-priority-and-interrupt-priority/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>


        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/2022/12/18/memory-ordering-cpu-cache/" rel="prev" title="cpu cache">
                  <i class="fa fa-chevron-left"></i> cpu cache
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/2022/12/25/FreeRTOS-interrupt-nesting/" rel="next" title="FreeRTOS-硬件中断嵌套模型和内核临界区">
                  FreeRTOS-硬件中断嵌套模型和内核临界区 <i class="fa fa-chevron-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2025</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">feng xun</span>
</div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">552k</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">11:30</span>
  </span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  





  





</body>
</html>
